
#include<stdio.h>
#include<string>
#include <iostream>
#include <string.h>
#include "sm4.h"
using namespace std;

//Note::填充0xFF限制，不能用于多次加密(>=2)密文直接解密，需要特殊处理

int encrypt( unsigned char* in, unsigned char* out, long length)
{

    static const uint8_t k[SM4_BLOCK_SIZE] = {
    0x01, 0xd3, 0x45, 0x67, 0xaa, 0xab, 0xcd, 0xef,
    0xf0, 0xcc, 0xba, 0x98, 0x76, 0x51, 0xdf, 0x1d
    };

    SM4_KEY key;
    ossl_sm4_set_key(k, &key);

    int remainder = length % SM4_BLOCK_SIZE;
    int cycle = length / SM4_BLOCK_SIZE;

    uint8_t block[SM4_BLOCK_SIZE] = {0};
    for(int i = 0; i < cycle; i ++)
    {
        memcpy(block, (in + i*SM4_BLOCK_SIZE), SM4_BLOCK_SIZE);

        ossl_sm4_encrypt(block, block, &key);
        //ossl_sm4_encrypt(block, block, &key);

        memcpy((out + i*SM4_BLOCK_SIZE), block, SM4_BLOCK_SIZE);
    }

    if(remainder > 0)
    {
        memcpy(block, (in + (cycle)*SM4_BLOCK_SIZE), remainder);
        memset((block + remainder), 0xff, SM4_BLOCK_SIZE-remainder); //fill by 0xff
        ossl_sm4_encrypt(block, block, &key);
        //ossl_sm4_encrypt(block, block, &key);
        memcpy((out + (cycle)*SM4_BLOCK_SIZE), block, SM4_BLOCK_SIZE);
    }
#if 0
    static const uint8_t input[SM4_BLOCK_SIZE] = {
        0x54, 0xa3, 0x45, 0xb7, 0x89, 0xab, 0xcd, 0xef,
        0xfe, 0xdc, 0xba, 0x98, 0x76, 0xc4, 0x32, 0xf0
    };

    /*
     * This test vector comes from Example 1 of GB/T 32907-2016,
     * and described in Internet Draft draft-ribose-cfrg-sm4-02.
     */
    static const uint8_t expected[SM4_BLOCK_SIZE] = {
        0x68, 0x1e, 0xdf, 0x34, 0xd2, 0x06, 0x96, 0x5e,
        0x86, 0xb3, 0xe9, 0x4f, 0x53, 0x6e, 0x42, 0x46
    };

    /*
     * This test vector comes from Example 2 from GB/T 32907-2016,
     * and described in Internet Draft draft-ribose-cfrg-sm4-02.
     * After 1,000,000 iterations.
     */
    static const uint8_t expected_iter[SM4_BLOCK_SIZE] = {
        0x59, 0x52, 0x98, 0xc7, 0xc6, 0xfd, 0x27, 0x1f,
        0x04, 0x02, 0xf8, 0x04, 0xc3, 0x3d, 0x3f, 0x66
    };

    int i;
    SM4_KEY key;
    uint8_t block[SM4_BLOCK_SIZE];

    ossl_sm4_set_key(k, &key);
    memcpy(block, input, SM4_BLOCK_SIZE);

    ossl_sm4_encrypt(block, block, &key);
    

    for(int i = 0; i < SM4_BLOCK_SIZE; i++)
    {
        printf("0x%2x ",block[i]);  
        if(i == 7)
        printf("\n");      
    }
    printf("\n\n");    

    for (i = 0; i != 999999; ++i)
        ossl_sm4_encrypt(block, block, &key);

    for(int i = 0; i < SM4_BLOCK_SIZE; i++)
    {
        printf("0x%2x ",block[i]);  
        if(i == 7)
        printf("\n");      
    }
    printf("\n\n");   

    for (i = 0; i != 1000000; ++i)
        ossl_sm4_decrypt(block, block, &key);

    for(int i = 0; i < SM4_BLOCK_SIZE; i++)
    {
        printf("0x%2x ",block[i]);  
        if(i == 7)
        printf("\n");      
    }
    printf("\n\n");   

#endif


    return 1;
}


int decrypt( unsigned char* in, unsigned char* out, long length)
{

    static const uint8_t k[SM4_BLOCK_SIZE] = {
    0x01, 0xd3, 0x45, 0x67, 0xaa, 0xab, 0xcd, 0xef,
    0xf0, 0xcc, 0xba, 0x98, 0x76, 0x51, 0xdf, 0x1d
    };

    SM4_KEY key;
    ossl_sm4_set_key(k, &key);

    int remainder = length % SM4_BLOCK_SIZE;
    int cycle = length / SM4_BLOCK_SIZE;

    uint8_t block[SM4_BLOCK_SIZE] = {0};
    for(int i = 0; i < cycle; i ++)
    {
        memcpy(block, (in + i*SM4_BLOCK_SIZE), SM4_BLOCK_SIZE);

        ossl_sm4_decrypt(block, block, &key);
        //Note::使用0xFF填充尾端16bits弊端，如果使用循环加密的话会出现某一次的密文包含0xFF内容
        //使用时需要判断最后16bit的解密处理是否为最后一次，如果是去掉0xFF，如果不是正常解密
        //ossl_sm4_decrypt(block, block, &key);
        
        if(i == (cycle-1))
        {
            int j = 0;
            while(block[j] != 0xff)
            {
                j++;
            }
            memcpy((out + i*SM4_BLOCK_SIZE), block, j);
        }
        else
            memcpy((out + i*SM4_BLOCK_SIZE), block, SM4_BLOCK_SIZE);
    }


    return 0;
}


long resizeEncryptLength(long length)
{
    if(length % SM4_BLOCK_SIZE)
    {
        return (length/SM4_BLOCK_SIZE + 1)*SM4_BLOCK_SIZE;
    }

    return length;
}
bool encryptString(const string& in, string& out)
{
        if(in.empty())
        {
            return false;
        }

        long inlength = in.length();
        long outlength = resizeEncryptLength(inlength);
        cout << inlength << "->" << outlength << endl;

        //string orgStr(in);
        //out.reserve(outlength);
        //orgStr.append(outlength-inlength, 0xFF);

        char * res = new char[outlength];
        memset(res, 0, outlength);
        encrypt((unsigned char *)&in[0], (unsigned char *)&res[0], inlength);

        out.assign(res, outlength);
        delete [] res;
        cout << "OK " <<  endl;
        return true;
        
}

bool dencryptString(string in, string& res)
{

}

#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>


bool exit(const char* filename)
{
    struct stat checkfile;
    return (stat(filename, &checkfile) == 0);
}


int main(int argc, char** argv)
{
#if 0 //文件加解密
    if(argc == 1)
        return 0;


    string filename = argv[1];
    cout << filename << endl;

    //encrypt();
    if (!exit(filename.c_str()))
    {
        cout << "file not exit" << endl;
        return 0;
    }

    FILE *file = fopen(filename.c_str(), "r");
    if(!file)
    {
        cout << "open error" << endl;
        return 0;
    }

    fseek(file, 0L, SEEK_END);
    long length= ftell(file);
    fseek(file, 0L, SEEK_SET);

    if (length < 0)
    {
        cout << "length < 0" << endl;
        return 0;
    }

    int outlenght = length;
    if(length % SM4_BLOCK_SIZE)
    {
        outlenght = (length/SM4_BLOCK_SIZE + 1)*SM4_BLOCK_SIZE;
    }

    unsigned char* inbuffer = new unsigned char[length];
    unsigned char* out = new unsigned char[outlenght];
    memset(inbuffer, 0, length);
    memset(out, 0, outlenght);
    if(fread(inbuffer, length, 1, file) != 1)
    {
        cout << "read error" << endl;
        return 0;
    }
    fclose(file);

    if(*argv[2] == '1')
        encrypt(inbuffer, out, length);
    else
        decrypt(inbuffer, out, outlenght);

    FILE* outfile = fopen("tdst.lol", "w");

    if(fwrite(out, outlenght, 1, outfile) != 1)
    {
        cout << " write erro" << endl;
        return 0;
    }
    fclose(outfile);

#else //字符串加解密
    string tst = "ajdi ihaih 90\"dfdsfsd\"asdfakj gqwgzsfa[]U8uif97^&%*()^%";
    string res{};

    encryptString(tst, res);

    cout << res.size();
    for(int i = 0; i < res.size(); i++)
        printf("%2x ", res[i]);

    char * tt = new char[tst.length()];
    memset(tt, 0, tst.length());
    decrypt((unsigned char*)&res[0], (unsigned char*)&tt[0], res.length());

    string out;
    out.assign(tt, tst.length());
    delete [] tt;
    cout << out << endl;

#endif
    return 0;
}
